#ifndef STX_CORE_STRING_CONVERSIONS_HPP
#define STX_CORE_STRING_CONVERSIONS_HPP

#include <stx/config.hpp>
#include <sstream>
#include <string>
#include <typeinfo>

namespace stx {

template<class T, class char_type>
inline T&
from_string(const std::basic_string<char_type>& src, T& dst)
{
    std::basic_istringstream<char_type> ss(src);
    ss >> dst;
    if (ss.fail()) {
        throw std::bad_cast();
    }
    return dst;
}

template<class T, class char_type>
inline T
from_string(const std::basic_string<char_type>& src)
{
    T dst;
    from_string(src, dst);
    return dst;
}

template<class T, class char_type>
inline std::basic_string<char_type>&
str(const T& src, std::basic_string<char_type>& dst)
{
    std::basic_ostringstream<char_type> ss;
    ss << src;
    if (ss.fail()) {
        throw std::bad_cast();
    }
    dst = ss.str();
    return dst;
}

template<class T, class char_type>
inline std::basic_string<char_type>
to_basic_string(const T& src)
{
    std::basic_string<char_type> dst;
    str(src, dst);
    return dst;
}

template<class char_type>
class string_builder
{
public:
    
    typedef string_builder<char_type> this_type;
    typedef std::basic_string<char_type> string_type;
    typedef std::basic_ostringstream<char_type> stream_type;
    
    string_builder()
    {
    }
    
    string_builder(const this_type& t)
    {
        ss_.str(t.str());
    }
    
    template<class T>
    string_builder(const T& t)
    {
        ss_ << t;
    }
    
    template<class T>
    this_type& operator <<(const T& t)
    {
        ss_ << t;
        return *this;
    }
    
    //  For std::stringstream compatibility.
    string_type str() const
    {
        return ss_.str();
    }
    
    operator string_type() const
    {
        return str();
    }
    
private:
    
    stream_type ss_;
};

template<class output_stream, class char_type>
inline output_stream&
operator <<(output_stream& os, const string_builder<char_type>& s)
{
    os << s.str();
    return os;
}

template<class T>
inline std::string
str(const T& src)
{
    return to_basic_string<T, char>(src);
}

inline string_builder<char>
str()
{
    return string_builder<char>();
}

#ifdef STX_HAS_WCHAR_T

template<class T>
inline std::wstring
wstr(const T& src)
{
    return to_basic_string<T, wchar_t>(src);
}

inline string_builder<wchar_t>
wstr()
{
    return string_builder<wchar_t>();
}

#endif

#ifdef STX_HAS_UNICODE_CHARS

template<class T>
inline std::u16string
u16str(const T& src)
{
    return to_basic_string<T, char16_t>(src);
}

inline string_builder<char16_t>
u16str()
{
    return string_builder<char16_t>();
}

template<class T>
inline std::u32string
u32str(const T& src)
{
    return to_basic_string<T, char32_t>(src);
}

inline string_builder<char32_t>
u32str()
{
    return string_builder<char32_t>();
}

#endif

} // namespace stx

#endif // STX_CORE_STRING_CONVERSIONS_HPP
